#ifndef _Resource_H__
#define _Resource_H__

#include "SharedPtr.h"
#include "AtomicWrappers.h"

namespace PVM
{
	typedef unsigned long long int ResourceHandle;
	
	class ManualResourceLoader;
	
	class Resource : public StringInterface
	{
	public:
		PVM_AUTO_MUTEX
		class Listener
		{
		public:
			Listener() {}
			virtual ~Listener() {}
			
			virtual void backgroundLoadingComplete(Resource*) {}
			
			virtual void backgroundPreparingComplete(Resource*) {}
			
			virtual void loadingComplete(Resource*) {}
			
			virtual void preparingComplete(Resource*) {}
			
			virtual void unloadingComplete(Resource*) {}
		};
		
		enum LoadingState
		{
			LOADSTATE_UNLOADED,
			LOADSTATE_LOADING,
			LOADSTATE_LOADED,
			LOADSTATE_UNLOADING,
			LOADSTATE_PREPARED,
			LOADSTATE_PREPARING
		};
		
	protected:
		ResourceManager* mCreator;
		std::string mName;
		std::string mGroup;
		ResourceHandle mHandle;
		AtomicScalar<LoadingState> mLoadingState;
		volatile bool mIsBackgroundLoaded;
		size_t mSize;
		bool mIsManual;
		std::string mOrigin;
		ManualResourceLoader* mLoader;
		size_t mStateCount;
		
		typedef std::set<Listener*> ListenerList;
		ListenerList mListenerList;
		PVM_MUTEX(mListenerListMutex)
		
		Resource()
		: mCreator(0), mHandle(0), mLoadingState(LOADSTATE_UNLOADED),
		mIsBackgroundLoaded(false), mSize(0), mIsManual(0), mLoader(0)
		{
		}
		
		virtual void preLoadImpl(void) {}
		virtual void postLoadImpl(void) {}
		virtual void preUnloadImpl(void) {}
		virtual void postUnloadImpl(void) {}
		virtual void prepareImpl(void) {}
		virtual void unprepareImpl(void) {}
		virtual void loadImpl(void) = 0;
		virtual void unloadImpl(void) = 0;
		virtual size_t calculateSize(void) const = 0;
		
	public:
		Resource(ResourceManager* creator, const std::string& name, ResourceHandle handle,
				 const std::string& group, bool isManual = false, ManualResourceLoader* loader = 0);
		
		virtual ~Resource();
		
		virtual void prepare(bool backgroundThread = false);
		
		virtual void load(bool backgroundThread = false);
		
		virtual void reload(void);
		
		virtual bool isReloadable(void) const
		{
			return !mIsManual || mLoader;
		}
		
		virtual bool isManuallyLoaded(void) const
		{
			return mIsManual;
		}
		
		virtual void unload(void);
		
		virtual size_t getSize(void) const
		{
			return mSize;
		}
		
		virtual void touch(void);
		
		virtual const std::string& getName(void) const
		{
			return mName;
		}
		
		virtual ResourceHandle getHandle(void) const
		{
			return mHandle;
		}
		
		virtual bool isPrepared(void) const
		{
			return (mLoadingState.get() == LOADSTATE_PREPARED);
		}
		
		virtual bool isLoaded(void) const
		{
			return (mLoadingState.get() == LOADSTATE_LOADED);
		}
		
		virtual bool isLoading() const
		{
			return (mLoadingState.get() == LOADSTATE_LOADING);
		}
		
		virtual LoadingState getLoadingState() const
		{
			return mLoadingState.get();
		}
		
		virtual bool isBackgroundLoaded(void) const 
		{
			return mIsBackgroundLoaded;
		}
		
		virtual void setBackgroundLoaded(bool bl)
		{
			mIsBackgroundLoaded = bl;
		}
		
		virtual void escalateLoading();
		
		virtual void addListener(Listener* lis);
		
		virtual void removeListener(Listener* lis);
		
		virtual const std::string& getGroup(void) const
		{
			return mGroup;
		}
		
		virtual void changeGroupOwnership(const std::string& newGroup);
	
		virtual ResourceManager* getCreator(void) {return mCreator;}
		
		virtual const std::string& getOrigin(void) const
		{
			return mOrigin;
		}
		
		virtual void _notifyOrigin(const std::string& origin)
		{
			mOrigin = origin;
		}
		
		virtual size_t getStateCount() const
		{
			return mStateCount;
		}
		
		virtual void _dirtyState();
		
		virtual void _fireLoadingComplete(bool wasBackgroundLoaded);
		
		virtual void _firePreparingComplete(bool wasBackgroundLoaded);
		
		virtual void _fireUnloadingComplete(void);
	};
	
	typedef SharedPtr<Resource> ResourcePtr;
	
	class ManualResourceLoader
	{
	public:
		ManualResourceLoader() {}
		virtual ~ManualResourceLoader() {}
		
		virtual void prepareResource(Resource* resource)
		{
			(void)resource;
		}
		
		virtual void loadResource(Resource* resource) = 0;
	};
}

#endif
